package org.langcom.crypt;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;

public class DHBob {

	private KeyAgreement bobKeyAgree;
	private SecretKey bobDesKey;

	public byte[] getBobPublicKey(byte[] alicePubKeyEnc) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException {
		KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEnc);
		PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
		/*
		 * Bob gets the DH parameters associated with Alice's public key. He
		 * must use the same parameters when he generates his own key pair.
		 */
		DHParameterSpec dhParamSpec = ((DHPublicKey) alicePubKey).getParams();
		// Bob creates his own DH key pair
		System.out.println("BOB: Generate DH keypair ...");
		KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
		bobKpairGen.initialize(dhParamSpec);
		KeyPair bobKpair = bobKpairGen.generateKeyPair();
		// Bob creates and initializes his DH KeyAgreement object
		System.out.println("BOB: Initialization ...");
		bobKeyAgree = KeyAgreement.getInstance("DH");
		bobKeyAgree.init(bobKpair.getPrivate());
		// Bob encodes his public key, and sends it over to Alice.
		byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
		return bobPubKeyEnc;
	}

	public void bobObtainDesKey(byte[] alicePubKeyEnc) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, IllegalStateException {
		KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEnc);
		PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
		System.out.println("BOB: Execute PHASE1 ...");
		bobKeyAgree.doPhase(alicePubKey, true);
		// byte[] bobSharedSecret = bobKeyAgree.generateSecret();
		// int aliceLen = bobSharedSecret.length;
		// byte[] aliceSharedSecret = new byte[aliceLen];
		// bobKeyAgree.doPhase(alicePubKey, true);
		bobDesKey = bobKeyAgree.generateSecret("DES");
	}

	public byte[] encrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
		/*
		 * DES in ECB mode
		 */
		Cipher bobCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
		bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);
		byte[] ciphertext = bobCipher.doFinal(text.getBytes());
		return ciphertext;
	}

	public String decrypt(byte[] ciphertext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
		Cipher bobCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
		bobCipher.init(Cipher.DECRYPT_MODE, bobDesKey);
		byte[] recovered = bobCipher.doFinal(ciphertext);
		return new String(recovered);
	}
}
